package edu.xhu.lock.aspect;

import edu.xhu.lock.anno.AutoLock;
import edu.xhu.lock.exception.LockException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 自动锁切面
 * 处理加锁解锁逻辑
 */
@Slf4j
@Aspect
@Component
public class AutoLockAspect {
    @Resource
    private RedissonClient redissonClient;

    @Pointcut("@annotation(autoLock)")
    private void cutMethod(AutoLock autoLock) {

    }

    @Around(value = "cutMethod(autoLock)", argNames = "pjp,autoLock")
    public Object doLock(ProceedingJoinPoint pjp, AutoLock autoLock) throws Throwable {
        String[] keys = autoLock.key();
        String[] parameterNames = new StandardReflectionParameterNameDiscoverer()
                .getParameterNames(((MethodSignature) pjp.getSignature()).getMethod());
        Object[] args = pjp.getArgs();
        long waitTime = autoLock.waitTime();
        long leaseTime = autoLock.leaseTime();
        boolean res = false;
        RLock rLock = null;
        List<String> valueBySpel = getValueBySpel(keys[0], parameterNames, args, autoLock.keyConstant());
        //如果spel表达式是数组或者LIST 则使用红锁
        rLock = redissonClient.getLock(valueBySpel.get(0));
        //执行aop
        if (rLock != null) {
            try {
                if (waitTime == -1) {
                    res = true;
                    //一直等待加锁
                    rLock.lock(leaseTime, TimeUnit.SECONDS);
                } else {
                    //leaseTime=-1,表示默认30s，并开守护线程进行自动续期
                    //leaseTime=20s,表示20s后自动释放
                    res = rLock.tryLock(waitTime, -1, TimeUnit.SECONDS);
                }
                if (res) {
                    return pjp.proceed();
                } else {
                    throw new LockException("获取锁失败");
                }
            } finally {
                if (res && rLock.isLocked() && rLock.isHeldByCurrentThread()) {
                    rLock.unlock();
                }
            }
        }
        throw new LockException("获取锁失败");
    }

    public List<String> getValueBySpel(String key, String[] parameterNames, Object[] values, String keyConstant) {
        List<String> keys = new ArrayList<>();
        if (!key.contains("#")) {
            String s = "redisson:lock:" + key + keyConstant;
            log.info("没有使用spel表达式value->{}", s);
            keys.add(s);
            return keys;
        }
        //spel解析器
        ExpressionParser parser = new SpelExpressionParser();
        //spel上下文
        EvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], values[i]);
        }
        Expression expression = parser.parseExpression(key);
        Object value = expression.getValue(context);
        if (value != null) {
            if (value instanceof List) {
                List value1 = (List) value;
                for (Object o : value1) {
                    keys.add("redisson:lock:" + o.toString() + keyConstant);
                }
            } else if (value.getClass().isArray()) {
                Object[] obj = (Object[]) value;
                for (Object o : obj) {
                    keys.add("redisson:lock:" + o.toString() + keyConstant);
                }
            } else {
                keys.add("redisson:lock:" + value + keyConstant);
            }
        }
        log.info("spel表达式key={},value={}", key, keys);
        return keys;
    }

}
